package com.flying.invest.monitor.service;

import java.io.StringWriter;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.flying.common.annotation.DaoCreate;
import com.flying.common.annotation.DaoQuery;
import com.flying.common.annotation.DaoRemove;
import com.flying.common.annotation.DaoUpdate;
import com.flying.common.util.Codes;
import com.flying.common.util.DateUtils;
import com.flying.common.util.DateUtils.Field;
import com.flying.common.util.Utils;
import com.flying.framework.annotation.MethodInfo;
import com.flying.framework.annotation.Param;
import com.flying.framework.annotation.Service;
import com.flying.framework.context.ServiceContext;
import com.flying.framework.data.Data;
import com.flying.framework.exception.AppException;
import com.flying.framework.model.ActionResult;
import com.flying.framework.model.ModelFactory;
import com.flying.framework.model.QueryResult;
import com.flying.framework.security.Principal;
import com.flying.framework.service.AbstractService;
import com.flying.framework.util.SmartyOutputUtils;
import com.flying.invest.assets.model.Assets;
import com.flying.invest.assets.model.AssetsPrice;
import com.flying.invest.assets.service.AssetsPriceService;
import com.flying.invest.assets.service.AssetsService;
import com.flying.invest.invest.model.Investment;
import com.flying.invest.invest.service.InvestmentService;
import com.flying.invest.monitor.model.PriceMonitor;
import com.flying.invest.monitor.model.PriceMonitor.PriceType;
import com.flying.invest.monitor.model.PriceMonitor.PriceUnit;
import com.flying.invest.monitor.model.PriceMonitor.Stage;
import com.flying.invest.summary.model.SummaryInvestDaily;
import com.flying.invest.summary.service.SummaryInvestDailyService;

@Service(value="PriceMonitorService", desc="投资监控")
public class PriceMonitorService extends AbstractService{
	private InvestmentService investService;
	private AssetsService assetsService;
	private AssetsPriceService priceService;
	private SummaryInvestDailyService summaryService;
	
	public void setInvestmentService(InvestmentService s) {
		this.investService = s;
	}
	public void setAssetsService(AssetsService s) {
		this.assetsService = s;
	}
	public void setAssetsPriceService(AssetsPriceService s) {
		this.priceService = s;
	}
	public void setSummaryInvestDailyService(SummaryInvestDailyService s) {
		this.summaryService = s;
	}
        
	@MethodInfo("新增")
	@DaoCreate(entity=PriceMonitor.ENTITY_NAME)
	public PriceMonitor create(
			@Param(value=PriceMonitor.NAME, required=true,	desc="名称") String name,
			@Param(value=PriceMonitor.REMARKS, required=false,	desc="说明") String remarks,
			@Param(value=PriceMonitor.INVEST_ID, required=false,	desc="投资项目ID") Long invest_id,
			@Param(value=PriceMonitor.ASSETS_ID, required=false,	desc="投资品种") Long assets_id,
			@Param(value=PriceMonitor.USER_ID, required=false,	desc="所属用户") Long user_id,
			@Param(value=PriceMonitor.FROM_DATE, required=false,	desc="开始日期") Date from_date,
			@Param(value=PriceMonitor.PRICE_TYPE, required=false,	desc="监控价格", enumClass=PriceType.class) Integer price_type,
			@Param(value=PriceMonitor.STAGE, required=true,	desc="阶段", enumClass=Stage.class) Integer stage,
			@Param(value=PriceMonitor.PRICE_UNIT, required=false,	desc="价格单位", enumClass=PriceUnit.class) Integer price_unit,
			@Param(value=PriceMonitor.STAGE_PRICE1, required=false,	desc="阶段价格1") Double stage_price1,
			@Param(value=PriceMonitor.MONITOR_PRICE1, required=false,	desc="监控价格1") Double monitor_price1,
			@Param(value=PriceMonitor.STAGE_PRICE2, required=false,	desc="阶段价格2") Double stage_price2,
			@Param(value=PriceMonitor.MONITOR_PRICE2, required=false,	desc="监控价格2") Double monitor_price2,
			@Param(value=PriceMonitor.STAGE_PRICE3, required=false,	desc="阶段价格3") Double stage_price3,
			@Param(value=PriceMonitor.MONITOR_PRICE3, required=false,	desc="监控价格3") Double monitor_price3,
			@Param(value=PriceMonitor.CREATOR, required=false,	desc="创建人") String creator,
			@Param(value=PriceMonitor.CREATE_TIME, required=false,	desc="创建时间") Date create_time
		) {
		if(price_type == PriceType.COST_PRICE.value()) {
			if(invest_id == null) {
				throw new AppException("0101", "成本价要求选择投资项目！");
			} else {
				Investment v = this.investService.findById(invest_id);
				assets_id = v.getAssetsId();
				if(from_date == null) {
					from_date = v.getCreateTime();
				}
			}
		}
		if(price_type == PriceType.MARKET_PRICE.value() && assets_id == null) {
			throw new AppException("0101", "市价要求选择投资项目！");
		}
		
		Principal p = ServiceContext.getContext().getPrincipal();
		if(user_id == null) {
			user_id = Long.parseLong(p.getId());
		}
		creator = p.getName();
		return ModelFactory.create(PriceMonitor.class, 
				PriceMonitor.ASSETS_ID, assets_id,
				PriceMonitor.USER_ID, user_id,
				PriceMonitor.FROM_DATE, from_date,
				PriceMonitor.PRICE_TYPE, price_type == null?PriceType.COST_PRICE.value(): price_type,
				PriceMonitor.PRICE_UNIT, price_unit == null?PriceUnit.RATE.value(): price_unit,
				PriceMonitor.CREATOR, creator,
				PriceMonitor.CREATE_TIME, new Date());
	}

	@MethodInfo("修改")
	@DaoUpdate(entity=PriceMonitor.ENTITY_NAME)
	public ActionResult update(
			@Param(value=PriceMonitor.MONITOR_ID, required=true,	desc="监控ID") Long monitor_id,
			@Param(value=PriceMonitor.NAME, required=true,	desc="名称") String name,
			@Param(value=PriceMonitor.REMARKS, required=false,	desc="说明") String remarks,
			@Param(value=PriceMonitor.INVEST_ID, required=false,	desc="投资项目ID") Long invest_id,
			@Param(value=PriceMonitor.ASSETS_ID, required=false,	desc="投资品种") Long assets_id,
			@Param(value=PriceMonitor.USER_ID, required=true,	desc="所属用户") Long user_id,
			@Param(value=PriceMonitor.FROM_DATE, required=false,	desc="开始日期") Date from_date,
			@Param(value=PriceMonitor.PRICE_TYPE, required=false,	desc="监控价格", enumClass=PriceType.class) Integer price_type,
			@Param(value=PriceMonitor.STAGE, required=false,	desc="阶段", enumClass=Stage.class) Integer stage,
			@Param(value=PriceMonitor.PRICE_UNIT, required=false,	desc="价格单位", enumClass=PriceUnit.class) Integer price_unit,
			@Param(value=PriceMonitor.STAGE_PRICE1, required=false,	desc="阶段价格1") Double stage_price1,
			@Param(value=PriceMonitor.MONITOR_PRICE1, required=false,	desc="监控价格1") Double monitor_price1,
			@Param(value=PriceMonitor.STAGE_PRICE2, required=false,	desc="阶段价格2") Double stage_price2,
			@Param(value=PriceMonitor.MONITOR_PRICE2, required=false,	desc="监控价格2") Double monitor_price2,
			@Param(value=PriceMonitor.STAGE_PRICE3, required=false,	desc="阶段价格3") Double stage_price3,
			@Param(value=PriceMonitor.MONITOR_PRICE3, required=false,	desc="监控价格3") Double monitor_price3,
			@Param(value=PriceMonitor.UPDATOR, required=false,	desc="修改人") String updator,
			@Param(value=PriceMonitor.UPDATE_TIME, required=false,	desc="修改时间") Date update_time
		) {
		if(price_type == PriceType.COST_PRICE.value()) {
			if(invest_id == null) {
				throw new AppException("0101", "成本价要求选择投资项目！");
			} else {
				Investment v = this.investService.findById(invest_id);
				assets_id = v.getAssetsId();
				if(from_date == null) {
					from_date = v.getCreateTime();
				}
			}
		}
		if(price_type == PriceType.MARKET_PRICE.value()) {
			if(assets_id == null) {
				throw new AppException("0101", "市价要求选择投资项目！");
			} else {
				if(from_date == null) {
					from_date = DateUtils.add(new Date(), Field.MONTH, -3);
				}
			}
		}
		
		Principal p = ServiceContext.getContext().getPrincipal();
		if(user_id == null) {
			user_id = Long.parseLong(p.getId());
		}
		updator = p.getName();
		return ModelFactory.create(ActionResult.class, 
				PriceMonitor.ASSETS_ID, assets_id,
				PriceMonitor.USER_ID, user_id,
				PriceMonitor.FROM_DATE, from_date,
				PriceMonitor.PRICE_TYPE, price_type == null?PriceType.COST_PRICE.value: price_type,
				PriceMonitor.PRICE_UNIT, price_unit == null?PriceUnit.RATE.value(): price_unit,
				PriceMonitor.UPDATOR, updator,
				PriceMonitor.UPDATE_TIME, new Date());
	}

	@MethodInfo("删除")
	@DaoRemove(entity=PriceMonitor.ENTITY_NAME)
	public ActionResult remove(
			@Param(value=PriceMonitor.MONITOR_ID, required=true,	desc="监控ID") Long monitor_id
		) {
		return null;
	}

	@SuppressWarnings("unchecked")
	@MethodInfo("搜索")
	@DaoQuery(entity=PriceMonitor.ENTITY_NAME, pageable=true, modelClass=PriceMonitor.class)
	public QueryResult<PriceMonitor> findAll(
			@Param(value=PriceMonitor.NAME, required=false,	desc="名称", tag="LIKE") String name,
			@Param(value=PriceMonitor.INVEST_ID, required=false,	desc="投资项目ID") Long invest_id,
			@Param(value=PriceMonitor.ASSETS_ID, required=false,	desc="投资品种") Long assets_id,
			@Param(value=PriceMonitor.USER_ID, required=false,	desc="所属用户") Long user_id,
			@Param(value=Codes.PAGE_NUM, 	required=false, desc="分页页号，默认0") int page,
			@Param(value=Codes.PAGE_SIZE, 	required=false, desc="分页大小，默认10") int rows) {
		Principal p = ServiceContext.getContext().getPrincipal();
		if(user_id == null) {
			user_id = Long.parseLong(p.getId());
		}
		return ModelFactory.create(QueryResult.class, 
				PriceMonitor.USER_ID, user_id);
	}

	@MethodInfo("搜索")
	@DaoQuery(entity=PriceMonitor.ENTITY_NAME, pageable=false, modelClass=PriceMonitor.class)
	public QueryResult<PriceMonitor> findByUserId(
			@Param(value=PriceMonitor.USER_ID, required=true,	desc="所属用户") Long user_id) {
		return null;
	}

	@MethodInfo("查询")
	@DaoQuery(entity=PriceMonitor.ENTITY_NAME, single=true, throwsNotFoundException=true )
	public PriceMonitor findById(
			@Param(value=PriceMonitor.MONITOR_ID, required=true,	desc="监控ID") Long monitor_id
		) {
		return null;
	}

	//------
	public Data monitor(
			@Param(value=PriceMonitor.USER_ID, required=true,	desc="所属用户") Long user_id) {
		List<PriceMonitor> list = this.findByUserId(user_id).getRows();
		List<Item> rows = Utils.newArrayList();
		list.stream().filter(x -> x.getInvestId() != null).filter(x -> x.getInvestId()> 0 || x.getInvestId() == -1).forEach(x -> {
			if(x.getInvestId() > 0) {
				rows.add(monitorInvestment(x.getMonitorId(), x.getInvestId()));
			} else {
				List<Investment> invests = this.investService.findByUserId(user_id).getRows();
				invests.stream().filter(y -> y.getAssets().getTypeId() == 54).forEach(y -> {
					rows.add(this.monitorInvestment(x.getMonitorId(), y.getInvestId()));
				});;
			}
		});
		list.stream().filter(x -> x.getInvestId() == null).forEach(x -> {
			rows.add(monitorInvestment(x.getMonitorId(), x.getInvestId()));
		});
		for(var it = rows.iterator(); it.hasNext(); ) {
			if(it.next() == null) it.remove();
		}
		Data result = new Data(Codes.CODE, Codes.SUCCESS, 
				Codes.ROWS, rows.stream().sorted().map(x -> x.content).collect(Collectors.toList()));
		return result;
	}
	
	private Item monitorInvestment(
			@Param(value=PriceMonitor.MONITOR_ID, required=true,	desc="监控ID") Long monitor_id,
			@Param(value=PriceMonitor.INVEST_ID, required=false,	desc="投资项目ID") Long invest_id) {
		PriceMonitor monitor = this.findById(monitor_id);
		invest_id = invest_id == null?monitor.getInvestId(): invest_id;;
		//
		Map<String, Object> map = Utils.newHashMap();
		map.put("monitor", monitor);
		//
		if(monitor.getPriceType() == PriceType.MARKET_PRICE.value()) {
			Assets assets = this.assetsService.findById(monitor.getAssetsId());
			map.put("assets", assets);
			
			AssetsPrice price = this.priceService.findTopPriceByAssetsId(monitor.getAssetsId(), monitor.getFromDate(), new Date());
			map.put("price", price);
			//
			if(monitor.getPriceUnit() == PriceUnit.PRICE.value()) {
				if(monitor.getStage() == Stage.RISE.value() || monitor.getStage() == Stage.FELL.value()) {
					if(assets.getPrice() > monitor.getMonitorPrice1()) {
						return output(map, "monitor/assets_price_rise.tpl");
					} else if(assets.getPrice() < monitor.getMonitorPrice2()) {
						return output(map, "monitor/assets_price_fell.tpl");
					}
				} else {
					
				}
			} else if(monitor.getPriceUnit() == PriceUnit.RATE.value()) {
				
			}
		} else if(monitor.getPriceType() == PriceType.COST_PRICE.value()) {
			Investment invest = this.investService.findById(invest_id);
			if(invest.getStatus() == Investment.Status.FINISHED.value()) {
				return null;
			}
			map.put("invest", invest);
			
			Assets assets = this.assetsService.findById(invest.getAssetsId());
			map.put("assets", assets);
			//
			AssetsPrice price = this.priceService.findTopPriceByAssetsId(invest.getAssetsId(), invest.getCreateTime(), new Date());
			map.put("price", price);
			//
			if(monitor.getPriceUnit() == PriceUnit.PRICE.value() || 
					monitor.getPriceUnit() == PriceUnit.RATE.value()) {	//比对资产单价、比对市价总额
				if(monitor.getStage() == Stage.RISE.value() && assets.getPrice() > invest.getCostPrice()) {
					Double riseRate = (assets.getPrice() - invest.getCostPrice()) / invest.getCostPrice();
					if(riseRate > monitor.getMonitorPrice1()) {
						return output(map, "monitor/invest_price_rise.tpl");
					}
				} else if(monitor.getStage() == Stage.FELL.value() && assets.getPrice() < invest.getCostPrice()) {
					Double fellRate = (invest.getCostPrice() - assets.getPrice()) / invest.getCostPrice();
					if(fellRate > monitor.getMonitorPrice1()) {
						return output(map, "monitor/invest_price_fell.tpl");
					}
				} else if(monitor.getStage() == Stage.RISE_FELL.value() && 
						price.getMaxPrice() != null &&
						price.getMaxPrice() > invest.getCostPrice() && 
						price.getMaxPrice() > assets.getPrice()) {
					Double maxRiseRate = (price.getMaxPrice() - invest.getCostPrice()) / invest.getCostPrice();
					Double curRiseRate = (assets.getPrice() - invest.getCostPrice()) / invest.getCostPrice();
					Double downRate = (price.getMaxPrice() - assets.getPrice()) / price.getMaxPrice();
					List<SummaryInvestDaily> sums = this.summaryService.findAll(invest.getUserId(), invest.getInvestId(), null, 0, 1000).getRows();
					Double maxProfit = sums.stream().mapToDouble(d -> d.getProfit()).max().getAsDouble();
					map.put("maxRiseRate", maxRiseRate * 100);
					map.put("curRiseRate", curRiseRate * 100);
					map.put("downRate", downRate * 100);
					map.put("curProfit", invest.getAmount() - invest.getCost());
					map.put("maxProfit", maxProfit);
					if(downRate > monitor.getMonitorPrice1()) {
						return output(map, "monitor/invest_price_rise_fell.tpl");
					}
				} else if(monitor.getStage() == Stage.FELL_RISE.value() && 
						price.getMinPrice() < invest.getCostPrice() && 
						price.getMinPrice() < assets.getPrice()) {
					Double maxFellRate = (invest.getCostPrice() - price.getMinPrice()) / invest.getCostPrice();
					Double curFellRate = (invest.getCostPrice() - assets.getPrice()) / invest.getCostPrice();
					Double riseRate = maxFellRate - curFellRate;
					if(riseRate > monitor.getMonitorPrice1()) {
						return output(map, "monitor/invest_price_fell_rise.tpl");
					}
				}
			}
		}
		return null;
	}
	
	private Item output(Map<String, Object> map, String template) {
		StringWriter out = new StringWriter();
		Data data = new Data(map);
		SmartyOutputUtils.output(module, data, template, out);
		Double downRate = (Double)map.get("downRate");
		return new Item(out.toString(), downRate);
	}
	
	class Item implements Comparable<Item>{
		final String content;
		final Double downRate;
		
		public Item(String content, Double downRate) {
			this.content = content;
			this.downRate = downRate;
		}
		
		@Override
		public int compareTo(Item o) {
			return o.downRate.compareTo(this.downRate);
		}
	}
}